如何编写游戏对话系统加载和运行 Yarn 叙事脚本本页总览加载和运行 Yarn 叙事脚本 欢迎来到 YarnRunner 功能库使用教程。在本教程中,我们将指导您如何加载和运行您在前一教程中编写的 Yarn 叙事脚本。 1. 初始化 YarnRunner LuaTealTypeScriptYueScript 首先确保您已正确地导入所有必需的模块。以下是本教程示例中我们需要的模块:init.lualocal Content <const> = require("Content")local Path <const> = require("Path")local Node <const> = require("Node")local Director <const> = require("Director")local YarnRunner <const> = require("YarnRunner") 为了确保我们可以找到 Yarn 脚本,我们需要设置正确的搜索路径。如果 Yarn 脚本和程序模块在同一个目录下时我们可以增加如下代码:init.lualocal path = Path:getScriptPath(...)Content:insertSearchPath(1, path) 接下来,假设我们要加载的 Yarn 文件名为 "tutorial.yarn",起始节点的标题名称为 "Start",则编写如下代码:init.lualocal runner = YarnRunner("tutorial.yarn", "Start") 首先确保您已正确地导入所有必需的模块。以下是本教程示例中我们需要的模块:init.tllocal Content <const> = require("Content")local Path <const> = require("Path")local Node <const> = require("Node")local Director <const> = require("Director")local YarnRunner <const> = require("YarnRunner") 为了确保我们可以找到 Yarn 脚本,我们需要设置正确的搜索路径。如果 Yarn 脚本和程序模块在同一个目录下时我们可以增加如下代码:init.tllocal path = Path:getScriptPath(...)Content:insertSearchPath(1, path) 接下来,假设我们要加载的 Yarn 文件名为 "tutorial.yarn",起始节点的标题名称为 "Start",则编写如下代码:init.tllocal runner = YarnRunner("tutorial.yarn", "Start") 首先确保您已正确地导入所有必需的模块。以下是本教程示例中我们需要的模块:init.tsimport { Content, Path, Node, Director } from "Dora";import * as YarnRunner from "YarnRunner"; 为了确保我们可以找到 Yarn 脚本,我们需要设置正确的搜索路径。如果 Yarn 脚本和程序模块在同一个目录下时我们可以增加如下代码:init.tsconst path = Path.getScriptPath(...);Content.insertSearchPath(1, path); 接下来,假设我们要加载的 Yarn 文件名为 "tutorial.yarn",起始节点的标题名称为 "Start",则编写如下代码:init.tsconst runner = YarnRunner("tutorial.yarn", "Start"); 首先确保您已经导入所有必需的模块。init.yue_ENV = Doraimport "YarnRunner" 为了确保我们可以找到 Yarn 脚本,我们需要设置正确的搜索路径。如果 Yarn 脚本和程序模块在同一个目录下时我们可以增加如下代码:init.yuepath = Path\getScriptPath ...Content\insertSearchPath 1, path 接下来,假设我们要加载的 Yarn 文件名为 "tutorial.yarn",起始节点的标题名称为 "Start",则编写如下代码:init.yuerunner = YarnRunner "tutorial.yarn", "Start" 2. 执行和展示叙事内容 我们定义了一个 advance 函数,该函数可以读取并展示 Yarn 脚本中的文本内容或选项。根据叙事的内容,它还可以展示角色的名字: LuaTealTypeScriptYueScriptinit.lua-- advance 函数的参数是一个可选的整数,代表玩家选择的选项索引。-- 当第一次调用此函数或在不需要选择时,我们将传递 nillocal function advance(option) -- 函数首先调用 runner:advance(option) 来获取 Yarn 脚本的下一部分内容。 -- 这将返回两个值:一个动作类型和一个结果。 local action, result = runner:advance(option) -- 根据动作类型,选择如何处理结果。 if action == "Text" then -- 如果动作是 "Text",则结果会是一个 TextResult 对象, -- 其中包含文本内容和任何相关的标记(例如角色名)。 -- 检查标记,提取角色名(如果存在)并打印出文本内容。 local characterName = "" local marks = result.marks if marks then for i = 1, #marks do local mark = marks[i] if mark.name == "char" then characterName = mark.attrs.name .. ": " end end end print(characterName .. result.text) elseif action == "Option" then -- 如果动作是 "Option",则结果会是一个 OptionResult 对象, -- 其中包含了一个或多个选项。函数会遍历这些选项并打印它们, -- 玩家稍后可以选择它们。 for i, op in ipairs(result) do if op then print("[" .. tostring(i) .. "]: " .. op.text) end end else -- 对于其他动作(例如错误),函数将直接打印结果。 print(result) endendinit.tl-- advance 函数的参数是一个可选的整数,代表玩家选择的选项索引。-- 当第一次调用此函数或在不需要选择时,我们将传递 nillocal function advance(option?: integer) -- 函数首先调用 runner:advance(option) 来获取 Yarn 脚本的下一部分内容。 -- 这将返回两个值:一个动作类型和一个结果。 local action, result = runner:advance(option) -- 根据动作类型,选择如何处理结果。 if action == "Text" then -- 如果动作是 "Text",则结果会是一个 TextResult 对象, -- 其中包含文本内容和任何相关的标记(例如角色名)。 local textResult = result as YarnRunner.TextResult -- 检查标记,提取角色名(如果存在)并打印出文本内容。 local characterName = "" local marks = textResult.marks if not (marks is nil) then for i = 1, #marks do local mark = marks[i] if mark.name == "char" then characterName = tostring(mark.attrs.name) .. ": " end end end print(characterName .. textResult.text) elseif action == "Option" then -- 如果动作是 "Option",则结果会是一个 OptionResult 对象, -- 其中包含了一个或多个选项。函数会遍历这些选项并打印它们, -- 玩家稍后可以选择它们。 local optionResult = result as YarnRunner.OptionResult for i, op in ipairs(optionResult) do if op and not (op is boolean) then print("[" .. tostring(i) .. "]: " .. op.text) end end else -- 对于其他动作(例如错误),函数将直接打印结果。 print(result) endendinit.ts// advance 函数的参数是一个可选的整数,代表玩家选择的选项索引。// 当第一次调用此函数或在不需要选择时,我们将传递 nullfunction advance(option?: number): void { // 函数首先调用 runner.advance(option) 来获取 Yarn 脚本的下一部分内容。 // 这将返回两个值:一个动作类型和一个结果。 const [action, result] = runner.advance(option); // 根据动作类型,选择如何处理结果。 switch (action) { case "Text": // 如果动作是 "Text",则结果会是一个 TextResult 对象, // 其中包含文本内容和任何相关的标记(例如角色名)。 // 检查标记,提取角色名(如果存在)并打印出文本内容。 let characterName = ""; const marks = result.marks; if (marks) { for (const mark of marks) { if (mark.name === "char") { characterName = `${mark.attrs?.name}: `; } } } print(characterName + result.text); break; case "Option": // 如果动作是 "Option",则结果会是一个 OptionResult 对象, // 其中包含了一个或多个选项。函数会遍历这些选项并打印它们, // 玩家稍后可以选择它们。 const optionResult = result; for (let i = 0; i < optionResult.length; i++) { const op = optionResult[i]; if (op && op !== true) { print(`[${i}]: ${op.text}`); } } break; default: // 对于其他动作(例如错误),函数将直接打印结果。 print(result); break; }}init.yue-- advance 函数的参数是一个可选的整数,代表玩家选择的选项索引。-- 当第一次调用此函数或在不需要选择时,我们将传递 niladvance = (option) -> -- 函数首先调用 runner\advance 来获取 Yarn 脚本的下一部分内容。 -- 这将返回两个值:一个动作类型和一个结果。 action, result = runner\advance option -- 根据动作类型,选择如何处理结果。 switch action when "Text" -- 如果动作是 "Text",则结果会是一个 TextResult 对象, -- 其中包含文本内容和任何相 关的标记(例如角色名)。 -- 检查标记,提取角色名(如果存在)并打印出文本内容。 charName = "" if result.marks for mark in *result.marks switch mark when {name: attr, attrs: {:name}} charName = "#{name}: " if attr == "char" print charName .. result.text when "Option" -- 如果动作是 "Option",则结果会是一个 OptionResult 对象, -- 其中包含了一个或多个选项。函数会遍历这些选项并打印它们, -- 玩家稍后可以选择它们。 for i, op in ipairs result print "[#{i}]: #{op.text}" if op else -- 对于其他动作(例如错误),函数将直接打印结果。 print result 3. 启动叙事 要启动叙事,我们只需调用 advance 函数,不带任何参数: LuaTealTypeScriptYueScriptinit.luaadvance()init.tladvance()init.tsadvance();init.yueadvance! 4. 响应用户输入 为了能够让玩家与叙事互动,我们需要一个节点来捕获和响应用户的输入。我们创建一个节点,并为其指定一个名为 "go" 的信号槽。当这个信号被触发时,它将再次调用 advance 函数,并传递玩家选择的选项: LuaTealTypeScriptYueScriptinit.lualocal node = Node()node:gslot("go", function(option) advance(option)end)node:addTo(Director.entry)init.tllocal node = Node()node:gslot("go", function(option: nil | integer) advance(option)end)node:addTo(Director.entry)init.tsconst node = Node();node.gslot("go", (option: number | null) => { advance(option);});node.addTo(Director.entry);init.yuewith Node! \gslot "go", (option) -> advance option \addTo Director.entry 这里只是为了能快速进行测试而注册了一个全局的事件监听器。在实际的游戏开发中会需要通过编写UI交互的逻辑来完成使用。有了这个名叫“go”的全局事件监听器后。我们就可以通过打开 Dora SSR 的控制台界面,在下面的命令行中输入 emit 'go' 来继续推进对话,输入 emit 'go', 1 来选择对话分支完成交互式的测试运行了。 5. 添加自定义命令和状态 您可以通过给 YarnRunner 传递 command 和 state 参数来添加自定义命令和初始状态的变量。command 参数是一个包含可以执行命令的回调函数的 Lua 表,state 参数是一个包含预定义变量的表格。以下是一个示例: LuaTealTypeScriptYueScriptinit.lualocal runner = YarnRunner("tutorial.yarn", "Start", { playerScore = 100}, { print = print})init.tllocal runner = YarnRunner("tutorial.yarn", "Start", { playerScore = 100}, { print = print})init.tsconst runner = YarnRunner("tutorial.yarn", "Start", { playerScore: 100}, { print: print})init.yuerunner = YarnRunner "tutorial.yarn", "Start", { playerScore: 100}, { print: print} 然后在 Yarn 脚本中,您就可以通过使用 <<set $变量名 = 表达式>> 命令来改变初始变量的值,并通过自定义的命令 <<print $变量名>> 命令来打印变量的值: 测试对话节点<<set $playerScore = $playerScore + 200>><<print $playerScore>> 6. 结论 现在,您已经设置好了所有必要的代码来加载和运行 Yarn 叙事脚本。您可以运行上述脚本来开始您的互动叙事,玩家可以通过选择选项来与叙事互动。祝您创作愉快!